syntax = "proto3";

package nighthawk.client;

import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
import "validate/validate.proto";

// This proto represents that output format that Fortio expects when converted to JSON.
// Nighthawk can fill in this proto, and then unmarshal to the Fortio-compatible JSON.
// Therefore, this proto may not follow conventions. aip.dev/not-precedent
message FortioResult {
  // Disable validate because it doesn't work well with fields that are not all lowercase.
  // The field cases must match the final output JSON that fortio-ui expects.
  option (validate.disabled) = true;

  // From https://github.com/fortio/fortio/wiki/FAQ:
  // If using the command line, don't forget to use -a to auto save the json results in the current
  // data directory. And to use labels -labels "x y z" so the file name and the json include some
  // description of your run. You can later run fortio report to browse
  // graphically your results.
  // The Nighthawk equivalent of this field is structured as a repeated string, which will be
  // concatenated into this field, using ' ' as a separator.
  string Labels = 1;

  // Start time of the load test execution.
  google.protobuf.Timestamp StartTime = 2;

  // Configured qps
  uint32 RequestedQPS = 3;

  // Configured duration
  google.protobuf.Duration RequestedDuration = 4;

  // Effective qps
  double ActualQPS = 5;

  // Effective duration
  double ActualDuration = 6;

  // The configured number of used connections
  uint32 NumThreads = 7;

  // Latency histogram
  DurationHistogram DurationHistogram = 8;

  // Fortio tracks per-return-code. We group by 2xx,3xx, etc.
  map<string, uint64> RetCodes = 11;

  // Effective URI.
  string URL = 12;

  // (Unstructured) version. We serialize a representation of the Nighthawk version into this field.
  string Version = 13;

  // Was jitter used Y/N.
  bool Jitter = 14;

  // Run type. Can be "HTTP", "GRPC", and an empty string has also been observed.
  // We only set HTTP for now.
  string RunType = 15;

  // Sizes and HeaderSizes seems to mean different things in Fortio depending on if the go stdclient
  // or DIY fastclient was used. We populate this field with response body sizes, and HeaderSizes
  // with header sizes. Both are tracked in bytes. Note: we don't use full histograms here, but
  // rather rely on StreamingStatistic. This means we don't populate percentiles, and only populate
  // min/mean/max/etc with respect to DurationHistogram.
  DurationHistogram Sizes = 16;

  // See the 'Sizes' field for detals.
  DurationHistogram HeaderSizes = 17;

  // Some notes on fields that we do not populate today:

  // "Exactly" can be used to stop fortio after a fixed amount of replies.
  // We don't seem to need this, and going forward it might be hard/annoying to
  // translate this field as Nighthawk's concepts diverge a bit. If end up
  // needing to we can probably inspect the main phase and then figure out if there's a
  // termination predicate configured for a fixed number of responses observed
  // via stats. For now, however, skip this field.

  // We don't populate SocketCount (sometimes used to track #connections)

  // We don't populate AbortOn (a status code that may abort termination) for reasons
  // similar to Exactly.

  // Bytes sent should reflect the bytes sent after protocol serialization, compression, and
  // encryption are applied (i.e., the size of the buffers passed to the send() system call or its
  // equivalent).
  uint64 BytesSent = 18;

  // Bytes received should reflect the bytes read before decryption, de-compression, and protocol
  // de-serialization are applied (i.e., the size of bytes read from the recv() system call or its
  // equivalent).
  uint64 BytesReceived = 19;
}

message DurationHistogram {
  option (validate.disabled) = true;

  uint64 Count = 1;

  repeated DataEntry Data = 2;

  double Min = 3;

  double Max = 4;

  double Sum = 5;

  double Avg = 6;

  double StdDev = 7;

  repeated FortioPercentile Percentiles = 8;
}

message DataEntry {
  option (validate.disabled) = true;

  double Start = 1;

  double End = 2;

  double Percent = 3;

  uint64 Count = 4;
}

message FortioPercentile {
  option (validate.disabled) = true;

  double Percentile = 1;

  double Value = 2;
}
